失われた API
次のサウンド関数は Carbon
には含まれていません。
SndControl
SndStartFilePlay
SndPauseFilePlay
SndStopFilePlay
SndPlayDoubleBuffer
MACEVersion
Comp3to1
Exp1to3
Comp6to1
Exp1to6
AudioGetVolume
AudioSetVolume
AudioGetMute
AudioSetMute
AudioSetToDefaults
AudioGetInfo
AudioGetBass
AudioSetBass
AudioGetTreble
AudioSetTreble
AudioGetOutputDevice
AudioMuteOnEvent
SndRecordToFile
SPBRecordToFile
次の SndCommand は Carbon Sound Manager
ではサポートされていません。
initCmd
freeCmd
totalLoadCmd
loadCmd
freqDurationCmd
restCmd
freqCmd
ampCmd
timbreCmd
getAmpCmd
waveTableCmd
phaseCmd
rateCmd
continueCmd
doubleBufferCmd
getRateCmd
sizeCmd /* 廃止されたコマンド
*/
convertCmd /* 廃止された MACE コマンド
*/
コードが上述の呼び出しまたは SndCommand
を拠り所にしている場合、アプリケーションを Carbon
互換にするためにはコードの書き換えが必要になります。このテクニカルノートでは、これらの
API の機能を Carbon
互換コードを使って再現する方法について説明します。
|
削除された理由
ローレベル呼び出しは、互換性を維持しつつ、それらの機能をより効率的に実現する方法が存在するため削除されました。場合によっては、呼び出しそのものがすでに役割を終えているため、それを削除してもモダンコードベースに特に支障がないこともあります。
SndStartFilePlay、SPBRecordToFile
などのハイレベル呼び出しは、それらの機能が大部分
QuickTime
に包括されているため削除されました。これらのルーチンの代わりに
QuickTime
を使用すると、既存のコードベースに追加の作業やコードを加えることなく、プログラムの機能を向上させることができます
(たとえば、よりバリエーションに富んだサウンドファイルのリストを再生できます)。
上述の SndCommand
は、それらがもはやサポートされていないノンウェーブデータを対象に動作したり、それらの機能に取って代わるより新しい
SndCommand が存在するため削除されました。
|
削除された API のかつての機能
次に、それぞれの関数がかつて実行していた機能の概要を示します。詳細については、『Inside
Macintosh: Sound』を参照してください。
SndControl
- Sound Manager 2.0
で、サウンドハードウェアに関する情報を返していました。
SndStartFilePlay
- ディスクからファイルの再生を開始します。この関数は、基本的に
SndPlayDoubleBuffer を包むラッパーです。
SndPauseFilePlay
- SndStartFilePlay
によって再生されるファイルの再生状態を切り替えます。
SndStopFilePlay
- SndStartFilePlay
によって再生されるサウンドを停止します。
SndPlayDoubleBuffer
- サウンドを再生するときに、まずオーディオの一方のバッファを再生した後で、さらにもう一方のバッファを再生することで、一方のバッファを再生している間にもう一方のバッファにディスク
(またはディスク以外の場所)
からデータをロードすることを可能にします。この
API により、最小限の RAM
を使用するだけで、サイズの大きなサウンドファイルを再生できるようになります。
MACEVersion
- MACE
圧縮プログラム/伸張プログラムのバージョンを取得します。
Comp3to1
Exp1to3
- MACE 3:1
で圧縮されたサウンドを伸張します。
Comp6to1
Exp1to6
- MACE 6:1
で圧縮されたサウンドを伸張します。
AudioGetVolume
- Sound Manager
によって呼び出され、出力コンポーネントのボリュームを取得します。
AudioSetVolume
- Sound Manager
によって呼び出され、出力コンポーネントのボリュームを設定します。
AudioGetMute
- Sound Manager
によって呼び出され、出力コンポーネントの消音状態を取得します。
AudioSetMute
- Sound Manager
によって呼び出され、出力コンポーネントの消音状態を設定します。
AudioSetToDefaults
- Sound Manager
によって呼び出され、出力コンポーネントをそのデフォルトに戻します。
AudioGetInfo
- Sound Manager
によって呼び出され、出力コンポーネントに関する情報を取得します。
AudioGetBass
- Sound Manager
によって呼び出され、出力コンポーネントの低音ボリュームを取得します。
AudioSetBass
- Sound Manager
によって呼び出され、出力コンポーネントの低音ボリュームを設定します。
AudioGetTreble
- Sound Manager
によって呼び出され、出力コンポーネントの高音ボリュームを取得します。
AudioSetTreble
- Sound Manager
によって呼び出され、出力コンポーネントの高音ボリュームを設定します。
AudioGetOutputDevice
AudioMuteOnEvent
SndRecordToFile
- 同期操作によりサウンドデータをファイルに記録します。
SPBRecordToFile
- Rオプションの設定により、非同期的にサウンドデータをファイルに記録します。
ハ
initCmd
freeCmd
totalLoadCmd
- 廃止された SndControl
関数を使って送信され、既存のサウンド処理と
param2
で指定される初期化パラメータを持つ新規サウンドチャネルによる
CPU 負荷の総計をレポートしていました。
loadCmd
- 廃止された SndControl
関数を使って送信され、param2
で指定されるサウンドチャネルが必要とする CPU
処理パワーのパーセンテージをレポートしていました。
freqDurationCmd
- param2 で指定される音符を param1
で指定される時間にわたって再生します。
freqCmd
- サウンドの振動数 (またはピッチ)
を変更します。現在サウンドが再生されていないと、freqCmd
は、Sound Manager に param2
で指定される振動数で無期限に再生を開始させます。ある
soundCmd
によってインストールされたサンプリングサウンドのデータをループ再生するときに使用できました。
restCmd
ampCmd
timbreCmd
- 現在方形波データを使って定義されているサウンドの音質
(トーン) を変更します。
getAmpCmd
waveTableCmd
- 指定されたチャネルのボイスとしてウェーブテーブルをインストールします。
phaseCmd
rateCmd
- 現在再生中のサンプリングサウンドのレートを設定し、そのピッチと継続時間を効率的に変更します。アプリケーションはレートを
0
に設定して、再生中のサンプリングサウンドを休止できます。新しいレートは、22
kHz が基準と解釈される param2
で指定される値に設定されます。
continueCmd
doubleBufferCmd
- これは、SndPlayDoubleBuffer
によって使用されるコマンドです。
getRateCmd
- 現在再生中のサンプリングサウンドのサンプリングレートを取得します。チャネルの現在のレートは、サウンドコマンドの
param2 に渡すアドレスを持った Fixed
変数によって返されます。返される値は、rateCmd
サウンドコマンドと同様に、常に 22 kHz
のサンプリングレートを基準にしています。
sizeCmd /*obsolete command*/
convertCmd /*obsolete MACE
command*/
|
削除された API を置き換える方法
SndControl
- この呼び出しは Sound Manager 3.0
で廃止されました。Gestalt
呼び出しで置き換えてください。
SndStartFilePlay
- QuickTime
でシミュレートすることができます。
SndPauseFilePlay
- QuickTime を使用するときは、QuickTime
の制御コマンドを使用します。
SndStopFilePlay
- QuickTime を使用するときは、QuickTime
の制御コマンドを使用します。
SndPlayDoubleBuffer
- bufferCmd と callBackCmd
でシミュレートするか、ある程度は QuickTime
でもシミュレートできます。
MACEVersion
- この呼び出しは今後絶対に必要ありません。MACE
の現在のバージョンは 1.0.2
で、ここ何年にもわたって変更されていません。
Comp3to1
- SoundConverter API
の適切な呼び出しによって置き換えることができます。
Exp1to3
- SoundConverter API
の適切な呼び出しによって置き換えることができます。
Comp6to1
- SoundConverter API
の適切な呼び出しによって置き換えることができます。
Exp1to6
- SoundConverter API
の適切な呼び出しによって置き換えることができます。
AudioGetVolume
- SoundComponentGetInfo と siHardwareVolume
セレクタで置き換えることができます。
AudioSetVolume
- SoundComponentSetInfo と siHardwareVolume
セレクタで置き換えることができます。
AudioGetMute
- SoundComponentGetInfo と siHardwareMute
セレクタで置き換えることができます。
AudioSetMute
- SoundComponentSetInfo と siHardwareMute
セレクタで置き換えることができます。
AudioSetToDefaults
AudioGetInfo
- SoundComponentGetInfo
呼び出しと適切なセレクタで置き換えることができます。
AudioGetBass
- SoundComponentGetInfo と siHardwareBass
セレクタで置き換えることができます。
AudioSetBass
- SoundComponentSetInfo と siHardwareBass
セレクタで置き換えることができます。
AudioGetTreble
- SoundComponentGetInfo と siHardwareTreble
セレクタで置き換えることができます。
AudioSetTreble
- SoundComponentSetInfo と siHardwareTreble
セレクタで置き換えることができます。
AudioGetOutputDevice
AudioMuteOnEvent
SndRecordToFile
- Sequence Grabber (QuickTime)
でシミュレートできます。
SPBRecordToFile
- 非同期の場合は、SPBRecord と PBWriteAsync
の適切な呼び出しでシミュレートできます。また、QuickTime
を使って置き換えることもできます。
ハ
initCmd
freeCmd
totalLoadCmd
- 置き換える方法はありませんが、Sound
Manager 3.1
以降、このコマンドの正確さと有用性は失われました。
loadCmd
- 置き換える方法はありませんが、Sound
Manager 3.1
以降、このコマンドの正確さと有用性は失われました。
freqDurationCmd
- ピッチシフト機能は rateMultiplierCmd
で置き換えます。
freqCmd
- ピッチシフト機能と継続時間機能を
rateMultiplierCmd
で置き換えます。もはやサウンドをループ再生することはできません。サウンドの再生をループさせるには、bufferCmd
と callBackCmd を繰り返し使用します。
restCmd
- 方形波データサウンドおよびウェーブテーブルデータサウンドと置き換える方法は、Carbon
Sound Manager
ではサポートされていません。
ampCmd
timbreCmd
- 方形波データサウンドおよびウェーブテーブルデータサウンドと置き換える方法は、Carbon
Sound Manager
ではサポートされていません。
getAmpCmd
waveTableCmd
- ウェーブテーブルデータサウンドと置き換える方法は、Carbon
Sound Manager
ではサポートされていません。
phaseCmd
rateCmd
- 代わりに rateMultiplierCmd
を使用します。
continueCmd
doubleBufferCmd
- これは、SndPlayDoubleBuffer
によって使用されるコマンドです。直接的に置き換える方法はありません。ただし、bufferCmd
と callBackCmd
を使ってシミュレートすることはできます。詳細については、後述の「SndPlayDoubleBuffer
の置き換え」を参照してください。
getRateCmd
- 代わりに getRateMultiplierCmd
を使用します。
sizeCmd /* 廃止されたコマンド
*/
convertCmd /* 廃止された MACE コマンド
*/
|
SndPlayDoubleBuffer の置き換え
SndPlayDoubleBuffer
の動作が本来微妙なものであったため、SndPlayDoubleBuffer
の置き換えは若干トリッキーなものになります。SndPlayDoubleBuffer
を置き換えても、bufferCmd と callBackCmd
の単純なストリームは、本物の SndPlayDoubleBuffer
を期待するコードと厳密に同じ動作を行いません。
これは、本物の SndPlayDoubleBuffer
呼び出しがサウンドチャネルのキュー内の単一のコマンド
(doubleBufferCmd) であるのに対して、代替方法は 2
つのコマンドであるためです。さらに、doubleBufferCmd
はサウンドの再生が完了するまでチャネルのキュー内に留まりますが、bufferCmd
と callBackCmd
は常時追加または削除されます。このことは、キュー内で
doubleBufferCmd
の後に格納されているすべてのコマンドと、doubleBufferCmd
の後に格納されているコマンドに基づくコードにとっては大きな問題になります。このことは、SndPlayDoubleBuffer
のシミュレートの見通しを即座に否定するものではありませんが、コードのシミュレートに必要な作業がかなり複雑なものになることは否定できません。
第 1
の問題は、現在再生中のサウンドに関する情報が各サウンドチャネルに常時保持されている必要があるということです。この情報は、本物の
SndPlayDoubleBuffer を使用するときに Sound Manager
によって保持されます。
|
// 構造体
struct PerChanInfo {
QElemPtr qLink; /* 次のキューエントリ */
short qType; /* キュータイプ = 0 */
short stopping;
#if DEBUG
OSType magic;
#endif
SndCallBackUPP usersCallBack;
SndDoubleBufferHeader theParams;
CmpSoundHeader soundHeader;
};
typedef struct PerChanInfo PerChanInfo;
typedef struct PerChanInfo * PerChanInfoPtr;
// グローバル
Boolean gNMRecBusy;
NMRecPtr gNMRecPtr;
QHdrPtr gFreeList;
Ptr gSilenceTwos;
Ptr gSilenceOnes;
|
キュー構造体は、サウンドの形式、SndPlayDoubleBuffer
に渡すパラメータ、もともとサウンドチャネル内にあったコールバック関数
(コードをシミュレートする前にユーザが独自の目的で使用していた)、その他いくつかの保守情報などの、チャネルごとのサウンド情報を追跡するために使用されます。このキュー構造体により、割り込み時に
PBEnqueue
を使用してチャネル情報をキューに格納できるようになるため、タスク実行時にはチャネルごとの構造体と関連するメモリを破棄することができます。
シミュレーションはその単純な状態のマシンをセットアップする必要があり、さらにサウンドの第
1
バッファを再生する必要があります。そのコードは以下のようになります。
|
// これはタスク実行時に呼び出される必要がある
OSErr CarbonSndPlayDoubleBuffer (SndChannelPtr chan,
SndDoubleBufferHeaderPtr theParams) {
OSErr err;
CompressionInfo compInfo;
PerChanInfoPtr perChanInfoPtr;
SndCommand playCmd;
SndCommand callBack;
if (nil == chan) {
err = badChannel;
goto exit;
}
if (nil == theParams) {
err = paramErr;
goto exit;
}
if (nil == gFreeList) {
// いつ使用しなければならなくなるか (割り込み時に)
// 予想できないため、これは絶対に破棄できない
gFreeList = (QHdrPtr)NewPtrClear (sizeof (QHdr));
err = MemError ();
if (noErr != err) goto exit;
}
if (nil == gSilenceOnes) {
short i;
// いつ使用しなければならなくなるか (割り込み時に)
// 予想できないため、これは絶対に破棄できない
gSilenceOnes = NewPtr (kBufSize);
err = MemError ();
if (noErr != err) goto exit;
for (i = 0; i < kBufSize; i++) {
gSilenceOnes[i] = (char)0x80;
}
}
if (nil == gSilenceTwos) {
// いつ使用しなければならなくなるか (割り込み時に)
// 予想できないため、これは絶対に破棄できない
gSilenceTwos = NewPtrClear (kBufSize);
err = MemError ();
if (noErr != err) goto exit;
}
if (nil == gNMRecPtr) {
// いつ使用しなければならなくなるか (割り込み時に)
// 予想できないため、これは絶対に破棄できない
gNMRecPtr = (NMRecPtr)NewPtr (sizeof (NMRec));
err = MemError ();
if (noErr != err) goto exit;
// メモリの大部分 (すべてではなく) を破棄することになる
// NMProc 情報をセットアップする
gNMRecPtr->qLink = nil;
gNMRecPtr->qType = 8;
gNMRecPtr->nmFlags = 0;
gNMRecPtr->nmPrivate = 0;
gNMRecPtr->nmReserved = 0;
gNMRecPtr->nmMark = nil;
gNMRecPtr->nmIcon = nil;
gNMRecPtr->nmSound = nil;
gNMRecPtr->nmStr = nil;
gNMRecPtr->nmResp = NewNMProc (NMResponseProc);
gNMRecPtr->nmRefCon = 0;
}
perChanInfoPtr = (PerChanInfoPtr)NewPtr (sizeof (PerChanInfo));
err = MemError ();
if (noErr != err) goto exit;
// チャネル情報ごとの基本的な初期値
perChanInfoPtr->qLink = nil;
perChanInfoPtr->qType = 0; // 使用されない
perChanInfoPtr->stopping = 0;
#if DEBUG
perChanInfoPtr->magic = 'SANE';
#endif
perChanInfoPtr->theParams = *theParams;
// 独自のコールバック関数で上書きしようとしているため、
// サウンドに含まれるユーザのコールバック関数を覚えておく必要がある
perChanInfoPtr->usersCallBack = chan->callBack;
// SndPlayDoubleBuffer 呼び出しによって渡されるバッファの再生に使用する
// bufferCmd のサウンドヘッダをセットアップする
perChanInfoPtr->soundHeader.samplePtr =
(Ptr)(theParams->dbhBufferPtr[0]->dbSoundData);
perChanInfoPtr->soundHeader.numChannels =
theParams->dbhNumChannels;
perChanInfoPtr->soundHeader.sampleRate =
theParams->dbhSampleRate;
perChanInfoPtr->soundHeader.loopStart = 0;
perChanInfoPtr->soundHeader.loopEnd = 0;
perChanInfoPtr->soundHeader.encode = cmpSH;
perChanInfoPtr->soundHeader.baseFrequency = kMiddleC;
perChanInfoPtr->soundHeader.numFrames =
(unsigned long)theParams->dbhBufferPtr[0]->dbNumFrames;
// perChanInfoPtr->soundHeader.AIFFSampleRate = 0; // 使用されない
perChanInfoPtr->soundHeader.markerChunk = nil;
perChanInfoPtr->soundHeader.futureUse2 = nil;
perChanInfoPtr->soundHeader.stateVars = nil;
perChanInfoPtr->soundHeader.leftOverSamples = nil;
perChanInfoPtr->soundHeader.compressionID =
theParams->dbhCompressionID;
perChanInfoPtr->soundHeader.packetSize =
(unsigned short)theParams->dbhPacketSize;
perChanInfoPtr->soundHeader.snthID = 0;
perChanInfoPtr->soundHeader.sampleSize =
(unsigned short)theParams->dbhSampleSize;
perChanInfoPtr->soundHeader.sampleArea[0] = 0;
// サウンドは圧縮されているか? もしそうなら、
// theParams を SndDoubleBufferHeader2Ptr として取り扱う必要がある
if (0 != theParams->dbhCompressionID) {
// サウンドは圧縮されている
err = GetCompressionInfo (theParams->dbhCompressionID,
((SndDoubleBufferHeader2Ptr)theParams)->dbhFormat,
theParams->dbhNumChannels,
theParams->dbhSampleSize,
&compInfo);
if (noErr != err) goto exitDispose;
perChanInfoPtr->soundHeader.format = compInfo.format;
} else {
// サウンドは圧縮されていない
perChanInfoPtr->soundHeader.format = kSoundNotCompressed;
}
playCmd.cmd = bufferCmd;
playCmd.param1 = 0; // 使用されない
playCmd.param2 = (long)&perChanInfoPtr->soundHeader;
callBack.cmd = callBackCmd;
callBack.param1 = 0; // いっぱいにするバッファ、0 バッファ、1、0、...
callBack.param2 = (long)perChanInfoPtr;
// コールバック関数のポインタをサウンドチャネル構造体の
// 内部に直接インストールする
if (nil == gCarbonSndPlayDoubleBufferCallBackUPP) {
gCarbonSndPlayDoubleBufferCallBackUPP =
NewSndCallBackProc (CarbonSndPlayDoubleBufferCallBackProc);
}
chan->callBack = gCarbonSndPlayDoubleBufferCallBackUPP;
if (nil == gCarbonSndPlayDoubleBufferCleanUpUPP) {
#if !TARGET_API_MAC_CARBON
gCarbonSndPlayDoubleBufferCleanUpUPP =
NewSndCallBackProc (CarbonSndPlayDoubleBufferCleanUpProc);
#endif
}
err = SndDoCommand (chan, &playCmd, true);
if (noErr != err) goto exitDispose;
err = SndDoCommand (chan, &callBack, true);
if (noErr != err) goto exitDispose;
exit:
return err;
exitDispose:
if (nil != perChanInfoPtr)
DisposePtr ((Ptr)perChanInfoPtr);
goto exit;
}
|
Carbon には、SndDoubleBackProc に対する UPP
は存在しませんが、特に問題はありません。Carbon
のすべてのコードは PowerPC
コードであり、このコードは呼び出しプログラムとしてコンパイルされるため
(したがって、CFM<->Mach-O
呼び出し規約を考慮する必要はありません)、SndDoubleBackProc
は単純に正規の C
の関数ポインタとして取り扱われます。
ユーザのコードに現在空のバッファをいっぱいにすることを指示し、代替バッファの再生を開始するコールバック関数は以下のようになります。
|
static pascal void CarbonSndPlayDoubleBufferCallBackProc
(SndChannelPtr theChannel, SndCommand * theCallBackCmd) {
SndDoubleBufferHeaderPtr theParams;
SndDoubleBufferPtr emptyBuf;
SndDoubleBufferPtr nextBuf;
PerChanInfoPtr perChanInfoPtr;
SndCommand playCmd;
perChanInfoPtr = (PerChanInfoPtr)(theCallBackCmd->param2);
#if DEBUG
if (perChanInfoPtr->magic != 'SANE')
DebugStr("¥pBAD in CarbonSndPlayDoubleBufferCallBackProc");
#endif
if (true == perChanInfoPtr->stopping) goto exit;
theParams = &(perChanInfoPtr->theParams);
// 再生して、いっぱいにする必要のあるバッファ
emptyBuf = theParams->dbhBufferPtr[theCallBackCmd->param1];
// ready フラグをクリアする
emptyBuf->dbFlags ^= dbBufferReady;
// これは、いま再生するバッファ
nextBuf = theParams->dbhBufferPtr[!theCallBackCmd->param1];
// 準備ができているかどうか、またはしばらく待機する必要があるかどうかをチェックする
if (nextBuf->dbFlags & dbBufferReady) {
perChanInfoPtr->soundHeader.numFrames =
(unsigned long)nextBuf->dbNumFrames;
perChanInfoPtr->soundHeader.samplePtr = Ptr)(nextBuf->dbSoundData);
// ビットを反転して、次のバッファを指示する
theCallBackCmd->param1 = !theCallBackCmd->param1;
// これが最後のバッファでない場合は、ユーザのフィルルーチンを呼び出す
if (!(nextBuf->dbFlags & dbLastBuffer)) {
#if TARGET_API_MAC_CARBON
// ユーザの double back proc への関数ポインタを宣言する
void (*doubleBackProc)(SndChannel*, SndDoubleBuffer*);
// ユーザの double back proc を呼び出す
doubleBackProc = (void*)theParams->dbhDoubleBack;
(*doubleBackProc) (theChannel, emptyBuf);
#else
CallSndDoubleBackProc (theParams->dbhDoubleBack, theChannel, emptyBuf);
#endif
} else {
// 最後のバッファが終了したときに clean up proc を呼び出す
theChannel->callBack = gCarbonSndPlayDoubleBufferCleanUpUPP;
}
} else {
// バッファの準備ができるまで待機する必要がある。
// 本物の SndPlayDoubleBuffer はごく短いサイレントを再生して、
// ユーザがディスクからオーディオを読み込むのを待つため、
// ここでも同じことを行う
#if DEBUG
DebugStr ("¥p buffer is not ready!");
#endif
// 短いサイレントを再生するため、
// 再度 ready フラグをチェックできる
if (theParams->dbhSampleSize == 8) {
perChanInfoPtr->soundHeader.numFrames =
(UInt32)(kBufSize / theParams->dbhNumChannels);
perChanInfoPtr->soundHeader.samplePtr = gSilenceOnes;
} else {
perChanInfoPtr->soundHeader.numFrames =
(UInt32)(kBufSize / (theParams->dbhNumChannels *
(theParams->dbhSampleSize / 8)));
perChanInfoPtr->soundHeader.samplePtr = gSilenceTwos;
}
}
// コールバックコマンドを挿入する
InsertSndDoCommand (theChannel, theCallBackCmd);
// 次のバッファを再生する
playCmd.cmd = bufferCmd;
playCmd.param1 = 0;
playCmd.param2 = (long)&(perChanInfoPtr->soundHeader);
InsertSndDoCommand (theChannel, &playCmd);
exit:
return;
}
|
データの最後のバッファを再生したというシグナルをアプリケーションが発したときに一度だけ実行される、さらにもう
1
つのコールバック関数が必要です。この関数は、チャネルごとのサウンド情報をキューに格納するため、Notification
Manager
コールバックはチャネルごとのメモリを破棄することができます。
|
static pascal void CarbonSndPlayDoubleBufferCleanUpProc
(SndChannelPtr theChannel, SndCommand * theCallBackCmd) {
PerChanInfoPtr perChanInfoPtr;
perChanInfoPtr = (PerChanInfoPtr)(theCallBackCmd->param2);
#if DEBUG
if (perChanInfoPtr->magic != 'SANE') DebugStr("¥pBAD in
CarbonSndPlayDoubleBufferCleanUpProc");
#endif
// チャネルごとのデータを空いているキューに格納するため
// 後でクリーンアップできる
Enqueue ((QElemPtr)perChanInfoPtr, gFreeList);
// gFreeList をクリーンアップできるように
// Notification Manager ルーチンをインストールする必要がある
if (! OTAtomicSetBit (&gNMRecBusy, 0)) {
NMInstall (gNMRecPtr);
}
// 次のバッファが終了したときに呼び出されるように
// ユーザのコールバックプロシージャを戻す
theChannel->callBack = perChanInfoPtr->usersCallBack;
}
|
さらに、サウンドが終了した後でコードのシミュレーションが呼び出されることはないため、完了したサウンドに関連するメモリを破棄する必要があるときに、シミュレーションそのものの
(割り込み時に呼び出される)
後でメモリのクリーンアップを行うチャンスがなくなるという問題があります。この問題は、Notification
Manager
を使用して大部分のメモリを破棄することで部分的に緩和できる可能性がありますが、このために割り当てられた
Notification Manager
レコードを簡単にクリーンアップする方法はありません。
Notification Manager
コールバックのコードは以下のようになります。
|
static pascal void NMResponseProc (NMRecPtr nmReqPtr) {
PerChanInfoPtr perChanInfoPtr;
OSErr err;
NMRemove (nmReqPtr);
gNMRecBusy = false;
do {
perChanInfoPtr = (PerChanInfoPtr)gFreeList->qHead;
if (nil != perChanInfoPtr) {
err = Dequeue ((QElemPtr)perChanInfoPtr, gFreeList);
if (noErr == err) {
DisposePtr ((Ptr)perChanInfoPtr);
}
}
} while (nil != perChanInfoPtr && noErr == err);
}
|
既存のコードでは、SndPlayDoubleBuffer
を呼び出した後で、サウンドの再生に影響を与えることなく
callBackCmd
をインストールできることを前提としているため、SndPlayDoubleBuffer
を置き換えるコードでは、その bufferCmd と
callBackCmd
をサウンドキューの先頭に挿入する必要があります。このことは、サウンドチャネルのコマンドキューを直接的に取り扱うことを意味します。
このためのコードは以下のようになります。
|
static void InsertSndDoCommand (SndChannelPtr chan, SndCommand * newCmd) {
if (-1 == chan->qHead) {
chan->qHead = chan->qTail;
}
if (1 <= chan->qHead) {
chan->qHead--;
} else {
chan->qHead = chan->qTail;
}
chan->queue[chan->qHead] = *newCmd;
}
|
このことはさらに、オリジナルのコードが quietCmd
を使うことができるように、SndDoImmediate
をラップしなければらないことを意味しています。SndPlayDoubleBuffer
を呼び出した後に、quietCmd
がサウンドを停止して、インストールされていた何らかの
callBackCmd
に対する正しい関数を呼び出すことを確認するために、どのように
SndDoImmediate
をラップするのかを示すコードです。
|
// このルーチンは割り込み時に呼び出すことができるため、
// メモリの割り当てや解除は行わない
OSErr MySndDoImmediate (SndChannelPtr chan, SndCommand * cmd) {
PerChanInfoPtr perChanInfoPtr;
// これは、対象となるサウンドチャネルのいずれかで呼び出されているか?
// もしそうなら、ユーザのコマンドが実行できるように、
// われわれのコールバックを抜き取る必要がある
if (nil != gFreeList && gCarbonSndPlayDoubleBufferCallBackUPP ==
chan->callBack) {
if (quietCmd == cmd->cmd || flushCmd == cmd->cmd) {
// これがわれわれのチャネルであれば、
// callBackCmd がキューの先頭項目であることは明らか
perChanInfoPtr = (PerChanInfoPtr)
(chan->queue[chan->qHead].param2);
#if DEBUG
if (perChanInfoPtr->magic != 'SANE')
DebugStr("¥pBAD in MySndDoImmediate");
#endif
perChanInfoPtr->stopping = true;
Enqueue ((QElemPtr)perChanInfoPtr, gFreeList);
if (! OTAtomicSetBit (&gNMRecBusy, 0)) {
NMInstall (gNMRecPtr);
}
chan->callBack = perChanInfoPtr->usersCallBack;
}
}
return (SndDoImmediate (chan, cmd));
}
|
SndStartFilePlay
SndStartFilePlay
を使用して、制限された容量のメモリでサウンドリソース
(タイプが 'snd' のリソース)
を再生している場合、唯一のソリューションは、ReadPartialResource
を使って CarbonSndPlayDoubleBuffer
のコードを包むラッパーを書き、一度にサウンドリソースの一部のみを抽出することです。
QuickTime
には、完全にロードされていないデータを含むリソースハンドルを再生するオプションは用意されていません。QuickTime
もサウンドリソースを再生しますが、そのリソースがメモリ内に完全にロードされていることが前提になります。
一方、SndStartFilePlay
を使用してディスクからサウンドファイルを再生している場合は、QuickTime
を使用するのがおそらく最適です。SndStartFilePlay
とまったく同様に、特定の時点からサウンドの再生を開始するように
QuickTime
に指示することはできますが、サウンドがいつ終了するかを知るためにポーリングを行う必要があり、QuickTime
にはこの情報を取得するためのコールバックは存在しません。
QuickTime
にディスクからファイルを再生させるコードは以下のようになります。
|
OSErr err;
Movie theSound;
short fileRefNum;
err = OpenMovieFile (&theSpec, &fileRefNum, fsReadPerm);
if (noErr == err) {
err = NewMovieFromFile (&theSound, fileRefNum, 0, nil,
newMovieActive, nil);
}
if (noErr == err) {
GoToBeginningOfMovie (theSound);
}
if (noErr == err) {
StartMovie (theSound);
}
if (noErr == err) {
while (!IsMovieDone (theSound) {
MoviesTask (theSound, 0);
}
}
|
このコードは、FSSpec
によってポイントされたディスク上のファイルをオープンし、それをサウンドの先頭から末尾に向かって同期的に再生します。SndStartFilePlay
では、ファイルがすでにオープンされていて、それにファイル参照番号を渡す必要があるため、再生する必要のあるファイルへの
FSSpec
をあらかじめ用意しておく必要があります。
先頭以外の任意の場所からサウンドの再生を開始する場合は、QuickTime
の SetMovieTime
関数を使って、ムービー内の現在の時刻を設定します。
サウンドを非同期的に再生する場合は、theSound
をグローバルにし、メインイベントループの中でほぼ
1/4 秒ごとに MoviesTask
を呼び出して、任意のにせ信号を使ってムービーの実行を続ける必要があります。このことは、ユーザがマウスボタンを押し続けていたり、TrackDrag
を呼び出す必要があるために MoviesTask
を呼び出すことができないと、サウンドの再生が停止することを暗示しています。これを避けたい場合は、タスク時間を必要としない
CarbonSndPlayDoubleBuffer
のコードを使ってサウンドの再生を継続するようにコードを変換する必要があります。
|
|
SndRecordToFile and
SPBRecordToFile
SndRecordToFile
を使ってサウンドをディスクに録音している場合、最も簡単に
Carbon への移行を行うには QuickTime
を使用します。次のコードは、QuickTime の
Sequence Grabber
を使って、オーディオを同期的に記録する方法を示しています。
|
SGChannel sgSoundChan;
ComponentInstance sgSoundComp;
short numChannels,
sampleSize;
OSType compressionType,
inputSource;
err = SGInitialize (sgSoundComp);
if (err == noErr) {
err = SGNewChannel (sgSoundComp, SoundMediaType, &sgSoundChan);
}
if (err == noErr) {
err = SGSetChannelUsage (sgSoundChan, seqGrabRecord);
}
if (err == noErr) {
err = SGSetSoundInputRate (sgSoundChan, sampleRate);
}
if (err == noErr) {
err = SGSetSoundInputParameters
(sgSoundChan, sampleSize, numChannels, compressionType);
}
if (err == noErr) {
err = SPBSetDeviceInfo
(SGGetSoundInputDriver (sgSoundChan),
siOSTypeInputSource, &inputSource);
}
if (err == noErr) {
err = SGSoundInputDriverChanged (sgSoundChan);
}
if (err == noErr) {
err = SGSetDataOutput (sgSoundComp, &theSpec, seqGrabToDisk);
}
if (err == noErr) {
err = SGStartRecord (sgSoundComp);
}
if (err == noErr) {
EventRecord event;
Boolean done = false;
while (!done && err == noErr) {
WaitNextEvent (mDownMask | keyDownMask, &event, 6, nil);
err = SGIdle (sgSoundComp);
switch (event.what) {
case mouseDown:
case keyDown:
done = true;
break;
}
}
err = SGStop (sgSoundComp);
}
if (sgSoundComp != nil) {
err = CloseComponent (sgSoundComp);
}
|
Sequence Grabber
のユーザインタフェースを使用して、ユーザに録音の設定をさせたい場合は
(すでに独自のインタフェースを用意していない場合はこの方法をお勧めします)、siOSTypeInputSource
セレクタを含む
SGSetSoundInputRate、SGSetSoundInputParameters、SPBSetDeviceInfo、および
SGSoundInputDriverChanged
の呼び出しをスキップして、これらすべてを単一の
SGSettingsDialog
呼び出しに置き換えることができます。
非同期的に録音を行う必要がある場合は
(SndRecordToFile
は非同期録音を行いませんが、SPBRecordToFile
は行います)、sgSoundComp
をグローバルにして、メインイベントループから少なくとも
1/4 秒ごとに SGIdle
を呼び出す必要があります。
Sequence Grabber
を使用して録音を行ったときに提供される優れた機能の
1 つは、現在 Mac
にインストールされている任意の圧縮形式で録音ができるということです。したがって、選択肢は
MACE 3:1 および MACE 6:1
圧縮だけに制限されません。
|
|
要約
Carbon に含まれていない Sound Manager
関数の多くはすでにその使命を終えているものであるため、それらが削除されてもさしたる問題は発生しません。その他一部の関数に対しては、QuickTime
と少数の特別な Sound Manager Carbon
互換コードが必要な処理すべてを肩代わります。
QuickTime は使用が非常に容易であり
(少なくとも対象となるケースでは)、非 Carbon Sound
Manager
呼び出しの大部分の機能を備えているため、コードの他の部分を
Carbon
に変換することがそれほど困難でないときは、QuickTime
を使用するようにコード全体を変換してください。
SndPlayDoubleBuffer
を大々的に使用し、中断されないオーディオを配信するためにその割り込み駆動型の特質を必要とするアプリケーションの場合は、CarbonSndPlayDoubleBuffer
とそれに関連するいくつかの関数により、既存のコードベースに加える変更を最小限に抑えつつ同様の機能を実現することができます。
|
参考文献
ファイルのダウンロード
更新日: 2000 年 6 月 19 日
|